home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Magazin: Amiga-CD 1997 May & June
/
Amiga-CD 1997 #5-6.iso
/
emulatoren
/
frodov2.4
/
src
/
display.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-01-03
|
18KB
|
769 lines
/*
* Display.c - Darstellung der C64-Grafik,
* Handhabung des Emulatorfensters
*
* Copyright (C) 1994-1996 by Christian Bauer
*/
/*
* Anmerkungen:
* ------------
*
* - Die Farbpalette besteht aus den 16 C64-Farben, 16mal wiederholt.
* Dadurch spart man sich das Ausmaskieren der unteren 4 Bit bei den
* VIC-Farbcodes. Allerdings muß dieses bei der Chunky->Planar-
* Konvertierung für die Amiga-Chips erfolgen (der Algorithmus
* setzt voraus, daß die oberen Nibbles Null sind).
*/
#include <exec/types.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <graphics/copper.h>
#include <graphics/gfxmacros.h>
#include <hardware/custom.h>
#include <libraries/gadtools.h>
#include <devices/timer.h>
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/timer_protos.h>
#include <string.h>
#include "Display.h"
#include "SAM.h"
#include "Prefs.h"
#define CATCOMP_NUMBERS 1
#include "LocStrings.h"
// Aus Main.asm
extern struct Library *GfxBase;
extern struct Library *IntuitionBase;
extern void ShowPrefs(void);
extern void PutChProc(void);
extern char *GetStr(int strnum);
extern void ResetC64(void);
// Aus 6510.asm
extern struct Task *CPUTask;
extern void Pause6510(void);
extern void Resume6510(void);
extern ULONG InvokeSAMSet;
// Aus 6526.asm
extern void KeyPressed(char c);
extern WORD JoystickSwap;
// Aus 6569.asm
extern APTR CURRENTA5;
extern BYTE ChunkyBuf[];
extern UWORD SkipLatch;
extern UWORD LimitSpeed;
extern void graffiti_conv(UBYTE *p0, UBYTE *p1, UBYTE *p2, UBYTE *p3);
// Aus 6581.asm
extern void PauseSound(void);
extern void ResumeSound(void);
// Aus c2p4.asm
extern void c2p4(UBYTE *fBUFFER, UBYTE *fBUFFER_CMP, APTR *planes, struct Task *task, ULONG signals);
extern int Initc2p4(void);
extern void Exitc2p4(void);
// Prototypes
void open_double_buf(int type, int width, int height);
void set_graffiti_commands(void);
int handle_IDCMP(int done);
int handle_menu(int menu, int item, int done);
char has_gfx_39, has_gfx_40; // Flags für Version der graphics.library
int current_type, current_width, current_height;
struct Screen *the_screen;
struct Window *the_window;
struct VisualInfo *the_visual_info;
struct RastPort *the_rast_port;
struct ViewPort *the_view_port;
struct TextFont *topaz_font;
struct Menu *the_menus;
// Für WritePixelArray8()
struct BitMap *temp_bm;
struct BitMap v37_temp_bm;
struct RastPort temp_rp;
// Double Buffering
struct ScreenBuffer *scr_buf[2];
int inv_buf_num;
char using_db; // Flag: Double Buffering wird benutzt
// c2p4
APTR comparison_buf[2];
char c2p4_signal; // Signal: c2p4 fertig
LONGBITS c2p4_set;
char must_wait_for_c2p4; // Flag: Auf c2p4 muß gewartet werden
// Für Graffiti
struct UCopList *the_ucoplist;
extern struct Custom custom;
const UBYTE graffiti_pal_red[16] = {
0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8
};
const UBYTE graffiti_pal_green[16] = {
0, 0, 0, 0, 8, 8, 8, 8, 0, 0, 0, 0, 8, 8, 8, 8
};
const UBYTE graffiti_pal_blue[16] = {
0, 1, 8, 9, 0, 1, 8, 9, 0, 1, 8, 9, 0, 1, 8, 9
};
// Geschwindigkeitsanzeige
struct MsgPort *timer_port;
struct timerequest *timer_io;
const struct TextAttr topaz_attr = {
"topaz.font", 8, FS_NORMAL, 0
};
struct NewMenu new_menus[] = {
NM_TITLE, MSG_EMULATION_MENU, NULL, 0, 0, NULL,
NM_ITEM, MSG_SETTINGS_MENU, "P", 0, 0, NULL,
NM_ITEM, MSG_SAM_MENU, "M", 0, 0, NULL,
NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL,
NM_ITEM, MSG_QUIT_MENU, "Q", 0, 0, NULL,
NM_END, NULL, NULL, 0, 0, NULL
};
const UBYTE palette_red[16] = {
0, 15, 12, 0, 15, 0, 0, 15, 15, 8, 15, 4, 8, 8, 8, 12
};
const UBYTE palette_green[16] = {
0, 15, 0, 15, 0, 12, 0, 15, 8, 4, 8, 4, 8, 15, 8, 12
};
const UBYTE palette_blue[16] = {
0, 15, 0, 12, 15, 0, 12, 0, 0, 0, 8, 4, 8, 8, 15, 12
};
/*
* Screen und Fenster öffnen, alles vorbereiten
* 0: OK, 1: Fehler beim Screen-Öffnen, 2: Kein Speicher
*/
int OpenDisplay(int type, ULONG display_id, UWORD overscan, int width, int height)
{
int depth;
int i;
has_gfx_39 = (GfxBase->lib_Version >= 39);
has_gfx_40 = (GfxBase->lib_Version >= 40);
must_wait_for_c2p4 = FALSE;
using_db = FALSE;
current_type = type;
current_width = width;
current_height = height;
switch (type) {
case SCRTYPE_8BIT: depth = 8; break;
case SCRTYPE_4BIT: depth = 4; break;
case SCRTYPE_1BIT: depth = 1; break;
case SCRTYPE_GRAF:
depth = 4;
current_width = width = 640; current_height = height = 256;
display_id = PAL_MONITOR_ID | HIRES_KEY;
overscan = OSCAN_STANDARD;
break;
}
if (!(the_screen = OpenScreenTags(NULL,
SA_Width, width,
SA_Height, height,
SA_DisplayID, display_id,
SA_Overscan, overscan,
SA_Depth, depth,
SA_Quiet, TRUE,
SA_AutoScroll, TRUE,
TAG_DONE)))
return 1;
the_rast_port = &the_screen->RastPort;
the_view_port = &the_screen->ViewPort;
if (topaz_font = OpenFont(&topaz_attr))
SetFont(the_rast_port, topaz_font);
SetAPen(the_rast_port, 1);
if (!(the_visual_info = GetVisualInfo(the_screen, NULL)))
return 2;
if (!(the_menus = CreateMenus(new_menus, GTMN_FullMenu, TRUE, TAG_DONE)))
return 2;
LayoutMenus(the_menus, the_visual_info, GTMN_NewLookMenus, TRUE, TAG_DONE);
if (!(the_window = OpenWindowTags(NULL,
WA_Left, 0,
WA_Top, 0,
WA_Width, width,
WA_Height, height,
WA_CustomScreen, the_screen,
WA_IDCMP, IDCMP_RAWKEY | IDCMP_MENUPICK | IDCMP_MENUVERIFY,
WA_Backdrop, TRUE,
WA_Borderless, TRUE,
WA_NoCareRefresh, TRUE,
WA_Activate, TRUE,
WA_NewLookMenus, TRUE,
TAG_DONE)))
return 2;
SetMenuStrip(the_window, the_menus);
switch (type) {
case SCRTYPE_8BIT:
// Farbpalette laden
for (i=0; i<256; i++)
SetRGB4(the_view_port, i,
palette_red[i & 0xf], palette_green[i & 0xf], palette_blue[i & 0xf]);
// Temporären RastPort für WritePixelArray8() anlegen
if (has_gfx_39) {
if (!(temp_bm = AllocBitMap(width, 1, 8, 0, NULL)))
return 2;
} else {
InitBitMap(temp_bm = &v37_temp_bm, depth, width, 1);
for (i=0; i<8; i++)
if (!(v37_temp_bm.Planes[i] = AllocRaster(width, 1)))
return 2;
}
InitRastPort(&temp_rp);
temp_rp.BitMap = temp_bm;
CURRENTA5 = ChunkyBuf; // Wird nicht mehr verändert
break;
case SCRTYPE_4BIT:
if (!Initc2p4())
return 2;
// Speicher für Comparison Buffer holen
if (!(comparison_buf[0] = AllocVec(width * height, MEMF_PUBLIC | MEMF_CLEAR)))
return 2;
// Farbpalette laden
for (i=0; i<16; i++)
SetRGB4(the_view_port, i,
palette_red[i & 0xf], palette_green[i & 0xf], palette_blue[i & 0xf]);
CURRENTA5 = ChunkyBuf;
open_double_buf(type, width, height);
must_wait_for_c2p4 = FALSE;
break;
case SCRTYPE_1BIT:
SetRGB4(the_view_port, 0, 0, 0, 0); // schwarz
SetRGB4(the_view_port, 1, 15, 15, 15); // weiß
CURRENTA5 = the_rast_port->BitMap->Planes[0];
open_double_buf(type, width, height);
break;
case SCRTYPE_GRAF:
// Interne Farbpalette laden
for (i=0; i<16; i++)
SetRGB4(the_view_port, i,
graffiti_pal_red[i], graffiti_pal_green[i], graffiti_pal_blue[i]);
// Graffiti-Befehlssequenz erstellen und externe Farbpalette laden
set_graffiti_commands();
// Copperliste installieren
if (!(the_ucoplist = (struct UCopList *)AllocVec(sizeof(struct UCopList), MEMF_PUBLIC | MEMF_CLEAR)))
return 2;
CINIT(the_ucoplist, 0x258);
for (i=0; i<0x32; i++) {
CWAIT(the_ucoplist, i, 15);
CMOVE(the_ucoplist, custom.bplcon0, 0xc301); // Hires, 4 Bitplanes, Genlock Audio
}
CEND(the_ucoplist);
Forbid();
the_view_port->UCopIns = the_ucoplist;
Permit();
RethinkDisplay();
CURRENTA5 = ChunkyBuf;
break;
}
return 0;
}
/*
* Erste Bildschirmzeile mit Graffiti-Kommandos beschreiben
*/
void set_graffiti_commands(void)
{
int i;
UBYTE *p0 = the_rast_port->BitMap->Planes[0];
UBYTE *p1 = the_rast_port->BitMap->Planes[1];
UBYTE *p2 = the_rast_port->BitMap->Planes[2];
UBYTE *p3 = the_rast_port->BitMap->Planes[3];
*p0++ = 5; // Pixel Read Mask = 15
*p1++ = 15;
*p2++ = 0;
*p3++ = 0;
for (i=0; i<16; i++) {
*p0++ = 4; // Set Color
*p1++ = i;
*p2++ = 6; // Set RGB Value
*p3++ = palette_red[i] << 2;
*p0++ = 6;
*p1++ = palette_green[i] << 2;
*p2++ = 6;
*p3++ = palette_blue[i] << 2;
}
for (i=0; i<80-32-2; i++) {
*p0++ = 0; // NOP
*p1++ = 0;
*p2++ = 0;
*p3++ = 0;
}
*p0++ = 0; // NOP
*p1++ = 0;
*p2++ = 8; // Start Lores
*p3++ = 0;
}
/*
* Double Buffering einrichten
*/
void open_double_buf(int type, int width, int height)
{
using_db = FALSE;
if (IntuitionBase->lib_Version >= 39) {
if (!(scr_buf[0] = AllocScreenBuffer(the_screen, NULL, SB_SCREEN_BITMAP)))
return;
if (!(scr_buf[1] = AllocScreenBuffer(the_screen, NULL, SB_COPY_BITMAP)))
return;
WaitTOF(); WaitTOF();
inv_buf_num = 1;
if (type == SCRTYPE_4BIT) {
CURRENTA5 = ChunkyBuf;
if (!(comparison_buf[1] = AllocVec(width * height, MEMF_PUBLIC | MEMF_CLEAR)))
return;
} else {
CURRENTA5 = scr_buf[1]->sb_BitMap->Planes[0];
}
using_db = TRUE;
}
}
/*
* Screen und Fenster schließen
*/
void CloseDisplay(void)
{
int i;
if (current_type == SCRTYPE_4BIT)
Exitc2p4();
if (comparison_buf[0]) {
FreeVec(comparison_buf[0]);
comparison_buf[0] = NULL;
}
if (comparison_buf[1]) {
FreeVec(comparison_buf[1]);
comparison_buf[1] = NULL;
}
if (IntuitionBase->lib_Version >= 39) {
FreeScreenBuffer(the_screen, scr_buf[0]); // NULL ist OK
scr_buf[0] = NULL;
FreeScreenBuffer(the_screen, scr_buf[1]);
scr_buf[1] = NULL;
}
if (the_menus) {
ClearMenuStrip(the_window);
FreeMenus(the_menus);
the_menus = NULL;
}
if (the_window) {
CloseWindow(the_window);
the_window = NULL;
}
// Temporären RastPort schließen
if (temp_bm) {
if (has_gfx_39)
FreeBitMap(temp_bm);
else {
for (i=0; i<8; i++) {
FreeRaster(v37_temp_bm.Planes[i], current_width, current_height);
v37_temp_bm.Planes[i] = NULL;
}
}
temp_bm = NULL;
}
if (the_visual_info) {
FreeVisualInfo(the_visual_info);
the_visual_info = NULL;
}
if (topaz_font) {
CloseFont(topaz_font);
topaz_font = NULL;
}
if (the_screen) {
CloseScreen(the_screen);
the_screen = NULL;
}
}
/*
* Vom 6510-Task aus nötige Initialisierungen
* Wird vom 6510-Task aufgerufen
*/
void InitDisplayFrom6510(void)
{
// Signal für c2p4
c2p4_signal = AllocSignal(-1);
c2p4_set = 1 << c2p4_signal;
// TimerIO einrichten
if (timer_port = CreateMsgPort()) {
if (timer_io = CreateIORequest(timer_port, sizeof(struct timerequest))) {
OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)timer_io, 0);
// timer_io für Speed Limiter starten
timer_io->tr_node.io_Command = TR_ADDREQUEST;
timer_io->tr_time.tv_secs = 0;
timer_io->tr_time.tv_micro = SkipLatch * 20000; // 20ms pro Bild
SendIO((struct IORequest *)timer_io);
}
}
}
/*
* Vom 6510-Task aus nötige Aufräumarbeiten
* Wird vom 6510-Task aufgerufen
*/
void ExitDisplayFrom6510(void)
{
if (timer_io) {
if (!CheckIO((struct IORequest *)timer_io))
WaitIO((struct IORequest *)timer_io);
CloseDevice((struct IORequest *)timer_io);
DeleteIORequest((struct IORequest *)timer_io);
timer_io = NULL;
}
if (timer_port) {
DeleteMsgPort(timer_port);
timer_port = NULL;
}
// Ggf. auf Signal von c2p4 warten
if (must_wait_for_c2p4)
Wait(c2p4_set);
FreeSignal(c2p4_signal);
}
/*
* C64-Grafik neu darstellen (VBlank, Double Buffering)
* Wird vom 6510-Task aufgerufen
*/
struct timeval start_time, end_time;
char speed_str[20];
int percent;
void RedrawDisplay(void)
{
switch (current_type) {
// SCRTYPE_8BIT: WritePixelLine in 6569.asm
case SCRTYPE_4BIT: // Double Buffering und c2p-Konvertierung
if (must_wait_for_c2p4)
Wait(c2p4_set);
if (using_db) {
while (!ChangeScreenBuffer(the_screen, scr_buf[inv_buf_num]))
WaitTOF();
inv_buf_num ^= 1;
c2p4(ChunkyBuf, comparison_buf[inv_buf_num], scr_buf[inv_buf_num]->sb_BitMap->Planes, CPUTask, c2p4_set);
} else
c2p4(ChunkyBuf, comparison_buf[0], the_rast_port->BitMap->Planes, CPUTask, c2p4_set);
must_wait_for_c2p4 = TRUE;
CURRENTA5 = ChunkyBuf;
break;
case SCRTYPE_1BIT: // Double Buffering
if (using_db) {
while (!ChangeScreenBuffer(the_screen, scr_buf[inv_buf_num]))
WaitTOF();
inv_buf_num ^= 1;
CURRENTA5 = scr_buf[inv_buf_num]->sb_BitMap->Planes[0];
} else
CURRENTA5 = the_rast_port->BitMap->Planes[0];
break;
case SCRTYPE_GRAF: // Chunky->ModeX
graffiti_conv(the_rast_port->BitMap->Planes[0]+80, the_rast_port->BitMap->Planes[1]+80,
the_rast_port->BitMap->Planes[2]+80, the_rast_port->BitMap->Planes[3]+80);
CURRENTA5 = ChunkyBuf;
break;
}
if (timer_io) {
// timer_io abbrechen, wenn Speed Limiter aus ist
if (!LimitSpeed)
if (!CheckIO((struct IORequest *)timer_io))
AbortIO((struct IORequest *)timer_io);
WaitIO((struct IORequest *)timer_io);
// timer_io neu starten
timer_io->tr_node.io_Command = TR_ADDREQUEST;
timer_io->tr_time.tv_secs = 0;
timer_io->tr_time.tv_micro = SkipLatch * 20000; // 20ms pro Bild
SendIO((struct IORequest *)timer_io);
}
// Zeit seit letztem Aufruf ermitteln
GetSysTime(&end_time);
SubTime(&end_time, &start_time);
GetSysTime(&start_time);
// Ein Bild sollte 20ms dauern
percent = (20000 * 100 / end_time.tv_micro + 1) * SkipLatch;
if (current_type != SCRTYPE_GRAF) { // Auf Graffiti unleserlich
RawDoFmt("%ld%%", &percent, &PutChProc, speed_str);
Move(the_rast_port, 4, current_height-6);
Text(the_rast_port, speed_str, strlen(speed_str));
}
}
/*
* C64-Grafik nach hinten
*/
void EmulToBack(void)
{
ScreenToBack(the_screen);
ModifyIDCMP(the_window, IDCMP_RAWKEY);
ClearMenuStrip(the_window);
}
/*
* C64-Grafik nach vorne
*/
void EmulToFront(void)
{
ScreenToFront(the_screen);
ModifyIDCMP(the_window, IDCMP_RAWKEY | IDCMP_MENUPICK | IDCMP_MENUVERIFY);
SetMenuStrip(the_window, the_menus);
}
/*
* Event-Schleife des Fensters
*/
void EventLoop(void)
{
int done = FALSE;
ULONG signals;
while (!done) {
signals = Wait((1 << the_window->UserPort->mp_SigBit) | InvokeSAMSet);
if (signals & (1 << the_window->UserPort->mp_SigBit))
done = handle_IDCMP(done);
if (signals & InvokeSAMSet)
handle_menu(0, 1, done);
}
}
/*
* IDCMP-Messages handhaben
*/
int handle_IDCMP(int done)
{
struct IntuiMessage *msg;
ULONG class;
UWORD code;
UWORD menu_number;
struct MenuItem *item;
while (!done && (msg = GetMsg(the_window->UserPort))) {
class = msg->Class;
code = msg->Code;
if (class == IDCMP_MENUVERIFY)
Pause6510(); // Subtask stoppen
ReplyMsg((struct Message *)msg);
switch (class) {
case IDCMP_MENUPICK:
Resume6510(); // Subtask fortführen
if (current_type == SCRTYPE_GRAF)
set_graffiti_commands(); // Graffiti-Kommandos wurden durch Menü übermalt
menu_number = code;
while (!done && (menu_number != MENUNULL)) {
item = ItemAddress(the_window->MenuStrip, menu_number);
done = handle_menu(MENUNUM(menu_number), ITEMNUM(menu_number), done);
menu_number = item->NextSelect;
}
break;
case IDCMP_RAWKEY:
switch (code) {
case 0x59: // F10: Reset
ResetC64();
break;
case 0x5f: // Help: JoystickSwap umschalten
JoystickSwap = JoystickSwap ? 0 : -1;
break;
case 0x5e: // + (NK): SkipLatch erhöhen
SkipLatch++;
break;
case 0x4a: // - (NK): SkipLatch erniedrigen
if (SkipLatch > 1)
SkipLatch--;
break;
default:
KeyPressed(code);
}
break;
}
}
return done;
}
/*
* Menü wurde ausgewählt
*/
int handle_menu(int menu, int item, int done)
{
if (menu == 0) {
switch (item) {
case 0: // Einstellungen
Pause6510();
PauseSound();
EmulToBack();
ShowPrefs();
EmulToFront();
ResumeSound();
Resume6510();
break;
case 1: // SAM
Pause6510();
PauseSound();
EmulToBack();
SAM();
EmulToFront();
ResumeSound();
Resume6510();
break;
case 3: // Quit
done = TRUE;
break;
}
}
return done;
}
/*
* Requester anzeigen
*/
int ShowRequester(int text, int gads, APTR args)
{
struct EasyStruct es;
es.es_StructSize = sizeof(struct EasyStruct);
es.es_Flags = 0;
es.es_Title = GetStr(MSG_REQTITLE);
es.es_TextFormat = GetStr(text);
es.es_GadgetFormat = GetStr(gads);
return EasyRequestArgs(NULL, &es, NULL, args);
}
/*
* Lokalisierungen vornehmen
*/
void LocalizeDisplay(void)
{
int i;
for (i=0; new_menus[i].nm_Type != NM_END; i++)
if (new_menus[i].nm_Label != NM_BARLABEL)
new_menus[i].nm_Label = GetStr((int)new_menus[i].nm_Label);
}